<?php
// -----------------------------------------------------
// Created by : php-cache
// -----------------------------------------------------
// User: tf <1096392101@qq.com>
// -----------------------------------------------------
declare (strict_types=1);

namespace Gao\Cache\handler;


use Gao\Cache\Drive;

class FileHandler extends Drive
{

    /**
     * 配置参数
     * @var array
     */
    protected $options = [
        'expire'        => 1200,
        'cache_subdir'  => true,
        'prefix'        => 'file',
        'path'          => 'cache/',
        'hash_type'     => 'sha1',
        'data_compress' => false,
        'serialize'     => ['serialize','unserialize'],
    ];

    public function get($key, $default = null)
    {
        [$content,$expire] = $this->getRaw($key);

        return is_null($content) ? $default : $this->unserialize($content);
    }

    /**
     * 设置缓存数据
     * @param string $key
     * @param mixed $value
     * @param null $ttl
     * @return bool
     */
    public function set($key, $value, $ttl = null): bool
    {
        $file = $this->getCacheKey($key);

        $data = $this->serialize($value);
        //是否压缩
        if ($this->options['data_compress'] && function_exists('gzcompress')) {
            $data = gzcompress($data,4);
        }

        $is_dir = $this->createDir($file);
        if (!$is_dir) {
            return false;
        }

        $data   = "<?php\n//" . sprintf('%012d', $ttl) . "\n exit();?>\n" . $data;

        $result = file_put_contents($file, $data);

        if ($result) {
            clearstatcache();
            return true;
        }

        return false;
    }

    /**
     * 获取文件名
     * @param string $name
     * @return string
     */
    public function getCacheKey(string $name): string
    {
        $name = hash($this->options['hash_type'], $name);

        if ($this->options['cache_subdir']) {
            // 使用子目录
            $name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
        }

        if ($this->options['prefix']) {
            $name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
        }

        $path = $this->options['path'];
        if (!is_dir($path)) {
            mkdir($path,0775,true);
        }

        return $path . $name . '.php';
    }

    /**
     * 删除缓存
     * @param string $key
     * @return bool
     */
    public function delete($key): bool
    {
        return $this->unlink($this->getCacheKey($key));
    }

    /**
     * 清除缓存
     * @return bool
     */
    public function clear(): bool
    {
        $dirname = $this->options['path'] . $this->options['prefix'];

        $this->rmdir($dirname);

        return true;
    }

    /**
     * 是否存在
     * @param string $key
     * @return bool
     */
    public function has($key): bool
    {
        return $this->getRaw($key) !== null;
    }

    /**
     * 获取缓存数据
     * @param string $name 缓存标识名
     * @return array|null
     */
    protected function getRaw(string $name)
    {
        $filename = $this->getCacheKey($name);

        if (!is_file($filename)) {
            return;
        }

        $content = @file_get_contents($filename);

        if (false !== $content) {
            $expire = (int) substr($content, 8, 12);
            if (0 != $expire && time() - $expire > filemtime($filename)) {
                //缓存过期删除缓存文件
                $this->unlink($filename);
                return;
            }

            $content = substr($content, 32);

            if ($this->options['data_compress'] && function_exists('gzcompress')) {
                //启用数据压缩
                $content = gzuncompress($content);
            }

            return is_string($content) ? [$content,$expire] : null;
        }
    }

    /**
     * 判断文件是否存在后，删除
     * @access private
     * @param string $path
     * @return bool
     */
    private function unlink(string $path): bool
    {
        try {
            $bool = false;
            if (is_file($path) && unlink($path))
            {
                $bool = true;
            }
            return $bool;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * 创建文件夹
     * @param $filename
     * @return bool
     */
    private function createDir($filename): bool
    {
        $dir = dirname($filename);

        if (!is_dir($dir)) {
            try {
                mkdir($dir, 0755, true);
                return true;
            } catch (\Exception $e) {
                // 创建失败
                return false;
            }
        }

        return false;
    }

    /**
     * 删除文件夹
     * @param $dirname
     * @return bool
     */
    private function rmdir($dirname): bool
    {
        if (!is_dir($dirname)) {
            return true;
        }

        $items = new \FilesystemIterator($dirname);

        foreach ($items as $item) {
            if ($item->isDir() && !$item->isLink()) {
                $this->rmdir($item->getPathname());
            } else {
                $this->unlink($item->getPathname());
            }
        }

        @rmdir($dirname);
        return true;
    }
}